home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Users Group Library 1996 July / C-C++ Users Group Library July 1996.iso / vol_300 / 367_01 / futi14as.zoo / mvdir.c < prev    next >
C/C++ Source or Header  |  1990-09-04  |  6KB  |  245 lines

  1. /* mvdir -- rename directory
  2.    Copyright (C) 1990 Free Software Foundation, Inc.
  3.  
  4.    This program is free software; you can redistribute it and/or modify
  5.    it under the terms of the GNU General Public License as published by
  6.    the Free Software Foundation; either version 1, or (at your option)
  7.    any later version.
  8.  
  9.    This program is distributed in the hope that it will be useful,
  10.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.    GNU General Public License for more details.
  13.  
  14.    You should have received a copy of the GNU General Public License
  15.    along with this program; if not, write to the Free Software
  16.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  17.  
  18. /* Helper program for GNU mv on machines that lack the rename system call.
  19.  
  20.    Usage: mvdir from to
  21.  
  22.    FROM must be an existing directory.
  23.    TO must not exist, but its parent must exist.
  24.  
  25.    Must be setuid root.
  26.  
  27.    Ian Dall (ian@sibyl.eleceng.ua.oz.au)
  28.    and David MacKenzie (djm@ai.mit.edu) */
  29.  
  30. #include <stdio.h>
  31. #include <errno.h>
  32. #include <sys/types.h>
  33. #include <signal.h>
  34. #include "system.h"
  35.  
  36. #ifdef STDC_HEADERS
  37. #include <stdlib.h>
  38. #include <errno.h>
  39. #else
  40. char *malloc ();
  41.  
  42. extern int errno;
  43. #endif
  44.  
  45. #ifndef HIPRI
  46. #define HIPRI -10
  47. #endif
  48.  
  49. #ifdef DEBUG
  50. #define link(FROM, TO) (printf("Linking %s to %s\n", FROM, TO), 0)
  51. #define unlink(FILE) (printf("Unlinking %s\n", FILE), 0)
  52. #endif
  53.  
  54. /* The name this program was run with. */
  55. char *program_name;
  56.  
  57. char *basename ();
  58. char *fullpath ();
  59. char *parent_dir ();
  60. char *xmalloc ();
  61. void error ();
  62. void strip_trailing_slashes ();
  63.  
  64. void
  65. main (argc, argv)
  66.      int argc;
  67.      char **argv;
  68. {
  69.   char *from, *from_parent, *from_base;
  70.   char *to, *to_parent, *to_parent_path;
  71.   struct stat from_stats, to_stats;
  72.   char *slash, temp;
  73.   int i;
  74.  
  75.   program_name = argv[0];
  76.   if (argc != 3)
  77.     {
  78.       fprintf (stderr, "Usage: %s existing-dir new-dir\n", program_name);
  79.       exit (2);
  80.     }
  81.   from = argv[1];
  82.   to = argv[2];
  83.   strip_trailing_slashes (from);
  84.   strip_trailing_slashes (to);
  85.   from_parent = parent_dir (from);
  86.   to_parent = parent_dir (to);
  87.  
  88.   /* Make sure `from' is not "." or "..". */
  89.   from_base = basename (from);
  90.   if (!strcmp (from_base, ".") || !strcmp (from_base, ".."))
  91.     error (1, 0, "cannot rename `.' or `..'");
  92.   
  93.   /* Even with an effective uid of root, link fails if the target exists.
  94.      That is what we want, so don't unlink `to' first.
  95.      However, we do need to check that the directories that link and unlink
  96.      will modify exist and are writable by the user. */
  97.  
  98.   if (stat (from, &from_stats))
  99.     error (1, errno, "%s", from);
  100.   if ((from_stats.st_mode & S_IFMT) != S_IFDIR)
  101.     error (1, 0, "`%s' is not a directory", from);
  102.   if (access (from_parent, W_OK))
  103.     error (1, errno, "cannot write to `%s'", from_parent);
  104.   if (access (to_parent, W_OK))
  105.     error (1, errno, "cannot write to `%s'", to_parent);
  106.  
  107.   /* To prevent disconnecting the tree rooted at `from' from its parent,
  108.      quit if any of the directories in `to' are the same (dev and ino)
  109.      as the directory `from'. */
  110.   
  111.   slash = to_parent_path = fullpath (to_parent);
  112.   while (*slash)
  113.     {
  114.       slash = index (slash, '/');
  115.       if (slash)
  116.     {
  117.       ++slash;
  118.       temp = *slash;
  119.       *slash = '\0';
  120.       if (stat (to_parent_path, &to_stats))
  121.         error (1, errno, "%s", to_parent_path);
  122.       *slash = temp;
  123.     }
  124.       else
  125.     {
  126.       /* Last element of path. */
  127.       slash = "";
  128.       if (stat (to_parent_path, &to_stats))
  129.         error (1, errno, "%s", to_parent_path);
  130.     }
  131.       
  132.       if (to_stats.st_dev == from_stats.st_dev
  133.       && to_stats.st_ino == from_stats.st_ino)
  134.     error (1, 0, "`%s' is an ancestor of `%s'", from, to);
  135.     }
  136.  
  137.   /* We can't make the renaming atomic, but we do our best. */
  138.   for (i = NSIG; i > 0; i--)
  139.     if (i != SIGKILL)
  140.       signal (i, SIG_IGN);
  141.   setuid (0);            /* Make real uid 0 so it is harder to kill. */
  142.   nice (HIPRI - nice (0));    /* Raise priority. */
  143.  
  144.   if (link (from, to))
  145.     error (1, errno, "cannot link `%s' to `%s'", from, to);
  146.   if (unlink (from))
  147.     error (1, errno, "cannot unlink `%s'", from);
  148.  
  149.   /* Replace the directory's `..' entry.  It used to be a link to
  150.      the parent of `from'; make it a link to the parent of `to' instead. */
  151.   i = strlen (to);
  152.   slash = xmalloc (i + 4);
  153.   strcpy (slash, to);
  154.   strcpy (slash + i, "/..");
  155.   if (unlink (slash) && errno != ENOENT)
  156.     error (1, errno, "cannot unlink `%s'", slash);
  157.   if (link (to_parent, slash))
  158.     error (1, errno, "cannot link `%s' to `%s'", to_parent, slash);
  159.  
  160.   exit (0);
  161. }
  162.  
  163. /* Return the name of the directory containing PATH. */
  164.  
  165. char *
  166. parent_dir (path)
  167.      char *path;
  168. {
  169.   char *dir;
  170.   char *base;
  171.   int length;
  172.  
  173.   base = rindex (path, '/');
  174.   if (base == NULL)
  175.     return ".";
  176.  
  177.   if (base > path)
  178.     base--;
  179.   length = base - path + 1;
  180.   dir = xmalloc (length + 1);
  181.   strncpy (dir, path, length);
  182.   dir[length] = '\0';
  183.   return dir;
  184. }
  185.  
  186. /* Return NAME with any leading path stripped off.  */
  187.  
  188. char *
  189. basename (name)
  190.      char *name;
  191. {
  192.   char *base;
  193.  
  194.   base = rindex (name, '/');
  195.   return base ? base + 1 : name;
  196. }
  197.  
  198. /* Return the full pathname (from /) of the directory DIR,
  199.    as static data. */
  200.  
  201. char *
  202. fullpath (dir)
  203.      char *dir;
  204. {
  205.   char wd[PATH_MAX + 2];
  206.   static char path[PATH_MAX + 2];
  207.  
  208.   if (getwd (wd) == NULL)
  209.     error (1, errno, "cannot get current directory");
  210.   if (chdir (dir))
  211.     error (1, errno, "%s", dir);
  212.   if (getwd (path) == NULL)
  213.     error (1, errno, "cannot get current directory");
  214.   if (chdir (wd))
  215.     error (1, errno, "%s", wd);
  216.  
  217.   return path;
  218. }
  219.  
  220. /* Allocate N bytes of memory dynamically, with error checking.  */
  221.  
  222. char *
  223. xmalloc (n)
  224.      unsigned n;
  225. {
  226.   char *p;
  227.  
  228.   p = malloc (n);
  229.   if (p == 0)
  230.     error (1, 0, "virtual memory exhausted");
  231.   return p;
  232. }
  233.  
  234. /* Remove any trailing slashes from STR. */
  235.  
  236. void
  237. strip_trailing_slashes (str)
  238.      char *str;
  239. {
  240.   int last = strlen (str) - 1;
  241.  
  242.   while (last > 0 && str[last] == '/')
  243.     str[last--] = '\0';
  244. }
  245.